using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;

namespace Ast.Core.Services.Extensions
{
    public static class ByteArrayExtensions
    {
        public static T ReadAs<T>(this byte[] buffer)
        {
            if (buffer == null) return default(T);

            var gcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            var retVal = Marshal.PtrToStructure<T>(gcHandle.AddrOfPinnedObject());
            gcHandle.Free();
            return retVal;
        }

        public static T[] ReadArrayAs<T>(this byte[] buffer, int length)
        {
            if (buffer == null) return new T[0];

            var size = Marshal.SizeOf<T>();
            var retVal = new List<T>(length);
            for (var i = 0; i < length; i++)
            {
                var bufferSegment = new byte[size];
                Array.Copy(buffer, i*size, bufferSegment, 0, size);
                var val = ReadAs<T>(bufferSegment);
                retVal.Add(val);
            }
            return retVal.ToArray();
        }

        public static T[] ReadArrayAs<T>(this byte[] buffer)
        {
            if (buffer == null) return new T[0];

            var size = Marshal.SizeOf<T>();
            var retVal = new List<T>();
            for (var i = 0;; i++)
            {
                var bufferSegment = new byte[size];
                Array.Copy(buffer, i*size, bufferSegment, 0, size);
                var val = ReadAs<T>(bufferSegment);
                if (val.Equals(default(T)))
                {
                    break;
                }
                retVal.Add(val);
            }
            return retVal.ToArray();
        }

        public static IEnumerable<int> IndexOfSequence(this byte[] buffer, byte[] pattern, int startIndex = 0)
        {
            if (buffer == null || buffer.Length == 0 || pattern == null || pattern.Length == 0) yield break;

            var i = Array.IndexOf(buffer, pattern[0], startIndex);
            while (i >= 0 && i <= buffer.Length - pattern.Length)
            {
                var segment = new byte[pattern.Length];
                Buffer.BlockCopy(buffer, i, segment, 0, pattern.Length);
                if (segment.SequenceEqual(pattern))
                {
                    yield return i;
                }
                i = Array.IndexOf(buffer, pattern[0], i + pattern.Length);
            }
        }
    }
}